home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 37 / Amiga Format CD37 (1999-02-16)(Future Publishing)(GB)(Track 1 of 3)[!][issue 1999-03].iso / -screenplay- / shareware / invasionforce / source / status.c < prev    next >
C/C++ Source or Header  |  1999-01-09  |  56KB  |  1,745 lines

  1. /*
  2.    status.c -- status report and examine city functions for Invasion Force
  3.  
  4.    This module creates a dialog box (or "requester window") allowing the
  5.    player to examine a city and set the production.
  6.    It also provides the status report, ship report, world view functions, etc.
  7.  
  8.    This source code is free.  You may make as many copies as you like.
  9. */
  10.  
  11. #include "global.h"
  12.  
  13. char *lv_text[11];        // text strings for my listview gadget
  14. int high_text, now_text;   // highest number string in gadget; selected string
  15.  
  16.  
  17.  
  18. void draw_listview()
  19. {
  20.    int ctr = 0;
  21.  
  22.    // create embossed frame
  23.    DrawBevelBox(rast_port,10,94,270,135,GT_VisualInfo,vi,TAG_END);
  24.  
  25.    // render in the text from my data structures
  26.    for (ctr=0; ctr<=high_text; ctr++)
  27.       if (ctr==now_text)
  28.          plot_text(13,96+ctr*12,lv_text[ctr],WHITE,BLACK,JAM2,&topaz11);
  29.       else
  30.          plot_text(13,96+ctr*12,lv_text[ctr],BLACK,LT_GRAY,JAM2,&topaz11);
  31.  
  32. }
  33.  
  34.  
  35. void update_listview(new_text)
  36. int new_text;
  37. {
  38.    if (new_text!=now_text) {
  39.       // deselect the old text
  40.       plot_text(13,96+now_text*12,lv_text[now_text],BLACK,LT_GRAY,JAM2,&topaz11);
  41.       // select the new
  42.       plot_text(13,96+new_text*12,lv_text[new_text],WHITE,BLACK,JAM2,&topaz11);
  43.       now_text = new_text;
  44.    }
  45. }
  46.  
  47.  
  48. // this function will show the relevant data for a city, and allow
  49. // the owner to change the production or anything else in it
  50.  
  51. void examine_city(metro)
  52. struct City *metro;
  53. {
  54.    // now I must delve into a lot of that Intuition junk
  55.    // take a deep breath -- I should be used to it by now...
  56.    int ctr;
  57.    struct Window *city_window = NULL;
  58.    struct Gadget *context, *okay_gad, *cancel_gad;
  59.    struct NewGadget button = {
  60.       62,245,  // leftedge, topedge
  61.       66,16,   // width, height
  62.       "Okay",  // text label
  63.       NULL,    // font
  64.       1,       // gadget ID
  65.       NULL,NULL,NULL
  66.    };
  67.  
  68.    // This table will help me look up which unit type the user has selected
  69.    // from the current list.
  70.    int lookup[13], index;
  71.  
  72.    // I won't mess with the actual production until I leave this window,
  73.    // so I will store the new value in new_product until then.
  74.    int new_product = metro->unit_type;
  75.    int old_product = metro->unit_type;
  76.  
  77.    if (metro->unit_type==-1)
  78.       new_product = RIFLE;    // default for newly captured cities
  79.  
  80.    // make sure user doesn't play with the map window now
  81.    SetPointer(map_window,BUSY_POINTER);
  82.    ModifyIDCMP(map_window,NULL);
  83.  
  84.    // create the [Okay] and [Cancel] buttons
  85.    if (!CreateContext(&context))
  86.       clean_exit(1,"Unable to create context gadget!");
  87.    button.ng_VisualInfo = vi;
  88.    button.ng_TextAttr = &topaz11bold;
  89.    okay_gad = CreateGadget(BUTTON_KIND,context,&button,TAG_END);
  90.    button.ng_LeftEdge = 166;
  91.    button.ng_GadgetText = "Cancel";
  92.    button.ng_TextAttr = &topaz11;
  93.    button.ng_Flags = NULL;
  94.    cancel_gad = CreateGadget(BUTTON_KIND,okay_gad,&button,TAG_END);
  95.  
  96.    // This block examines a city (metro) and builds a list detailing the
  97.    // items that can be produced there, and how long it will take to make each
  98.    // of them.  The resulting list is intended for use with the LISTVIEW gadget
  99.    // when viewing city information.
  100.    {
  101.       int industry, prod_time, under_const, ctr;
  102.       char pad[] = " ------------------";
  103.       BOOL seaport = port_cityP(metro);
  104.  
  105.       // factoring in both player production efficiency and city production
  106.       // efficiency gives us a final industry output value from 2 to 200.
  107.       industry = (PLAYER.prod*metro->industry)/50;
  108.  
  109.       // initialize the data for my improvised listview gadget
  110.       high_text = -1;
  111.       now_text = 0;
  112.  
  113.       // use this loop to hit each unit type from the wishbook
  114.       for (ctr=0, index=0; ctr<=10; ctr++)
  115.          if (wishbook[ctr].enabled) {
  116.  
  117.             // skip over ships if this is not a port city
  118.             if (seaport==FALSE && wishbook[ctr].ship_flag==TRUE)
  119.                continue;
  120.  
  121.             // build my lookup table
  122.             lookup[index++] = ctr;
  123.  
  124.             // basic time to produce
  125.             prod_time = wishbook[ctr].build/industry;
  126.  
  127.             // now see if we are already building this type unit
  128.             if (metro->unit_type==wishbook[ctr].type) {
  129.                prod_time -= metro->unit_wip/industry;
  130.                now_text = index-1;
  131.             } else
  132.                prod_time += prod_time/5;     // startup penalty
  133.  
  134.             // count number of such units already in production
  135.             {
  136.                struct City *metro = (struct City *)city_list.mlh_Head;
  137.                for (under_const=0; metro->cnode.mln_Succ; metro=(struct City *)metro->cnode.mln_Succ)
  138.                   if (metro->owner==player && metro->unit_type==ctr)
  139.                      under_const++;
  140.             }
  141.  
  142.             // generate the string
  143.             strcpy(foo," ");
  144.             strcat(foo,wishbook[ctr].name);
  145.             strncat(foo,pad,18-strlen(foo));
  146.             sprintf(bar," %3ld ------ %2ld ",prod_time,under_const);
  147.             strcat(foo,bar);
  148.  
  149.             // generate the listview text strings
  150.             lv_text[++high_text] = AllocVec((ULONG)(strlen(foo)+1),MEMF_CLEAR);
  151.             strcpy(lv_text[high_text],foo);
  152.          FI
  153.    }
  154.  
  155.    // do the window itself
  156.    {
  157.       // I am trying to position the window so it does not
  158.       // obscure the city itself.
  159.       int mx, my;
  160.       int left_edge, top_edge;
  161.  
  162.       log_to_abs(metro->col,metro->row,&mx,&my);
  163.       if ((metro->col-xoffs)<(disp_wd/2))
  164.          left_edge = mx+46;
  165.       else
  166.          left_edge = mx-305;
  167.       top_edge = my-10;
  168.  
  169.       city_window = OpenWindowTags(NULL,
  170.          WA_Gadgets,context,
  171.          WA_Title,         "City Information",
  172.          WA_CustomScreen,  map_screen,
  173.          WA_Top,top_edge,  WA_Left,left_edge,
  174.          WA_Height,267,    WA_Width,291,
  175.          WA_IDCMP,         IDCMP_GADGETUP|IDCMP_VANILLAKEY|IDCMP_RAWKEY|IDCMP_MOUSEBUTTONS,
  176.          WA_Flags,         WFLG_SMART_REFRESH|WFLG_DRAGBAR|WFLG_ACTIVATE,
  177.          TAG_END );
  178.       if (city_window==NULL)
  179.          clean_exit(1,"ERROR: Unable to open city information window!");
  180.       GT_RefreshWindow(city_window,NULL);
  181.    }
  182.  
  183.    // call to render in my improvised listview gadget
  184.    rast_port = city_window->RPort;
  185.    draw_listview();
  186.  
  187.    // print some text
  188.    sprintf(foo,"NAME: %-19s",metro->name);
  189.    plot_text(11,18,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  190.    sprintf(foo,"OWNER: %-25s",roster[metro->owner].name);
  191.    plot_text(11,31,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  192.    sprintf(foo,"INDUSTRY: %ld",metro->industry);
  193.    plot_text(11,44,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  194.    sprintf(foo,"LOCATION: %ld, %ld",metro->col,metro->row);
  195.    plot_text(11,55,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  196.    sprintf(foo,"CURRENTLY PRODUCING: %s",wishbook[new_product].name);
  197.    plot_text(11,231,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  198.    plot_text(149,72," Prod     Under",BLACK,LT_GRAY,JAM2,&topaz8);
  199.    plot_text(149,82," Time     Const",BLACK,LT_GRAY,JAM2,&topaz8);
  200.  
  201.    {  // handle the user actions here
  202.       struct IntuiMessage *message; // the message the IDCMP sends us
  203.  
  204.       // useful for interpreting IDCMP messages
  205.       UWORD code;
  206.       ULONG class;
  207.       APTR object;
  208.       UWORD qualifier;
  209.  
  210.       FOREVER {
  211.          WaitPort(city_window->UserPort);
  212.          while (message = GT_GetIMsg(city_window->UserPort)) {
  213.             code = message->Code;  // MENUNUM
  214.             object = message->IAddress;  // Gadget
  215.             class = message->Class;
  216.             qualifier = message->Qualifier;
  217.             GT_ReplyIMsg(message);
  218.             if (class==IDCMP_VANILLAKEY) {
  219.                switch ((char)code) {
  220.                   // left-Amiga V or RETURN are defaults for "Okay"
  221.                   case 'v':
  222.                      if ((qualifier & IEQUALIFIER_LCOMMAND)==0)
  223.                         break;
  224.                   case 13:    // RETURN/ENTER
  225.                      // show the button depressed
  226.                      show_depress(okay_gad,city_window->RPort);
  227.                      Delay(10L);
  228.                      metro->unit_type = new_product;
  229.                      goto exit_city_window;
  230.                   // left-Amiga B is default for "Cancel"
  231.                   case 'b':
  232.                      if ((qualifier & IEQUALIFIER_LCOMMAND)==0)
  233.                         break;
  234.                      show_depress(cancel_gad,city_window->RPort);
  235.                      Delay(10L);
  236.                      goto exit_city_window;
  237.                }
  238.             FI
  239.             if (class==IDCMP_RAWKEY) {
  240.                switch ((int)code) {
  241.                   case 76:    // up cursor
  242.                      if (now_text>0) {
  243.                         update_listview(now_text-1);
  244.                         new_product = lookup[now_text];
  245.                      }
  246.                      break;
  247.                   case 77:    // down cursor
  248.                      if (now_text<high_text) {
  249.                         update_listview(now_text+1);
  250.                         new_product = lookup[now_text];
  251.                      }
  252.                }
  253.             FI
  254.             if (class==IDCMP_MOUSEBUTTONS && code==SELECTDOWN) {
  255.                int new_text;
  256.                int x=message->MouseX, y=message->MouseY;
  257.  
  258.                // define the hit area for this pseudo-gadget
  259.                if (x<11 || x>(10+270-1) || y<94 || y>(94+135-1))
  260.                   break;
  261.  
  262.                // I have to find out what part of the gadget (i.e. which
  263.                // text string) was hit and then figure out which unit type
  264.                // it is referring to.  Fortunately, I have prepared a table
  265.                // for that purpose.
  266.                new_text = (y-95)/12;
  267.                if (new_text>high_text)
  268.                   break;
  269.  
  270.                update_listview(new_text);
  271.                new_product = lookup[now_text];
  272.             FI
  273.             if (class==IDCMP_GADGETUP) {
  274.                if (object==okay_gad) {
  275.                   metro->unit_type = new_product;
  276.                   goto exit_city_window;
  277.                FI
  278.                if (object==cancel_gad)
  279.                   goto exit_city_window;
  280.             FI
  281.          OD
  282.       OD
  283.    }
  284.    exit_city_window:
  285.  
  286.    // adjust WIP value to account for startup of a new product
  287.    if (old_product!=new_product)
  288.       metro->unit_wip = -wishbook[new_product].build/5;
  289.  
  290.    // in case this city was just taken (no prior production) and the
  291.    // user selected CANCEL, so it gets the default value of RIFLE
  292.    if (metro->unit_type==-1)  metro->unit_type=RIFLE;
  293.  
  294.    // now close up everything with the mapsize_window
  295.    CloseWindow(city_window);
  296.    FreeGadgets(context);
  297.  
  298.    // so that survey mode can pick up in the same spot
  299.    cursx = metro->col;
  300.    cursy = metro->row;
  301.  
  302.    // wipe out the listview text and clear the other data
  303.    for (ctr=0; ctr<=high_text; ctr++) {
  304.       FreeVec(lv_text[ctr]);
  305.       lv_text[ctr] = NULL;
  306.    OD
  307.  
  308.    rast_port = map_window->RPort;
  309.    ClearPointer(map_window);
  310.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  311. }
  312.  
  313.  
  314. void status_report(player)
  315. int player;
  316. {
  317.    struct Window *status_window = NULL;
  318.    struct Gadget *context, *okay_gad;
  319.    int wd=543, ht=246;
  320.    int left_edge, top_edge;
  321.  
  322.    struct NewGadget button = {
  323.       460,21,  // leftedge, topedge
  324.       66,16,   // width, height
  325.       "Okay",  // text label
  326.       NULL,    // font
  327.       1,       // gadget ID
  328.       NULL,NULL,NULL
  329.    };
  330.  
  331.    // make sure user doesn't play with the map window now
  332.    SetPointer(map_window,BUSY_POINTER);
  333.    ModifyIDCMP(map_window,NULL);
  334.  
  335.    if (!CreateContext(&context))
  336.       clean_exit(1,"Unable to create context gadget!");
  337.    button.ng_VisualInfo = vi;
  338.    button.ng_TextAttr = &topaz11bold;
  339.    okay_gad = CreateGadget(BUTTON_KIND,context,&button,TAG_END);
  340.  
  341.    left_edge = (map_screen->Width-wd)/2;
  342.    top_edge = (map_screen->Height-ht)/2;
  343.  
  344.    status_window = OpenWindowTags(NULL,
  345.       WA_Gadgets,       context,
  346.       WA_Title,         "War Report",
  347.       WA_CustomScreen,  map_screen,
  348.       WA_Top,           top_edge,
  349.       WA_Left,          left_edge,
  350.       WA_Height,        ht,
  351.       WA_Width,         wd,
  352.       WA_IDCMP,         IDCMP_GADGETUP|IDCMP_VANILLAKEY,
  353.       WA_Flags,         WFLG_SMART_REFRESH|WFLG_DRAGBAR|WFLG_ACTIVATE,
  354.       TAG_END );
  355.    if (status_window==NULL)
  356.       clean_exit(1,"ERROR: Unable to open status report window!");
  357.    GT_RefreshWindow(status_window,NULL);
  358.    rast_port = status_window->RPort;
  359.  
  360.    // this section is where I calculate and print the report info
  361.    {
  362.       struct City *metro;
  363.       struct Unit *unit;
  364.       int ctr, x, y, explored, all_cities=0, my_cities=0,
  365.          uuc[11],    // units under construction
  366.          usc[11],    // units soonest completion
  367.          uic[11];    // unit in combat
  368.  
  369.       for (ctr=0; ctr<11; ctr++) {
  370.          uuc[ctr] = 0;
  371.          usc[ctr] = -1;
  372.          uic[ctr] = 0;
  373.       }
  374.  
  375.       metro = (struct City *)city_list.mlh_Head;
  376.       for (; metro->cnode.mln_Succ; metro=(struct City *)metro->cnode.mln_Succ) {
  377.          all_cities++;
  378.          if (metro->owner==player) {
  379.             int industry, prod_time, utype=metro->unit_type;
  380.             my_cities++;
  381.             uuc[utype]++;
  382.             industry = (PLAYER.prod*metro->industry)/50;
  383.             prod_time = wishbook[utype].build/industry-metro->unit_wip/industry;
  384.             if (prod_time<usc[utype] || usc[utype]<0)
  385.                usc[utype] = prod_time;
  386.          }
  387.       }
  388.  
  389.       unit =  (struct Unit *)unit_list.mlh_Head;
  390.       for (; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ)
  391.          if (unit->owner==player)
  392.             uic[unit->type]++;
  393.  
  394.       explored = 0;
  395.       for (x=0; x<width; x++)
  396.          for (y=0; y<height; y++)
  397.             if (get(PLAYER.map,x,y)!=HEX_UNEXPLORED)
  398.                explored++;
  399.  
  400.       // print some text
  401.       sprintf(foo,"Turn #%ld    Player #%ld    %s",turn,player,PLAYER.name);
  402.       plot_text(11,18,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  403.       plot_text(11,36,
  404.          "                                   World",
  405.          BLACK,LT_GRAY,JAM2,&topaz11);
  406.       plot_text(11,48,
  407.          "         Captured   Exist         Explored",
  408.          BLACK,LT_GRAY,JAM2,&topaz11);
  409.       sprintf(foo,"CITIES    %3ld       %3ld            %3ld",
  410.          my_cities, all_cities, (explored*100)/(width*height));
  411.       strcat(foo,"%");
  412.       plot_text(11,60,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  413.  
  414.       plot_text(116,84,"    Under      Soonest    Are In    We've    We've",
  415.          BLACK,LT_GRAY,JAM2,&topaz11);
  416.       plot_text(116,96,"Construction  Completion  Combat  Destroyed   Lost",
  417.          BLACK,LT_GRAY,JAM2,&topaz11);
  418.  
  419.       for (ctr=0;ctr<11;ctr++) {
  420.          if (usc[ctr]>=0)
  421.             sprintf(foo,"%-11s      %3ld          %3ld       %3ld       %3ld      %3ld",
  422.                wishbook[ctr].name,
  423.                uuc[ctr],usc[ctr],uic[ctr],PLAYER.eud[ctr],PLAYER.ulc[ctr]);
  424.          else
  425.             sprintf(foo,"%-11s      %3ld          N/A       %3ld       %3ld      %3ld",
  426.                wishbook[ctr].name,
  427.                uuc[ctr],uic[ctr],PLAYER.eud[ctr],PLAYER.ulc[ctr]);
  428.          if (wishbook[ctr].enabled==FALSE)
  429.             sprintf(foo,"%-11s      N/A          N/A       %3ld       %3ld      %3ld",
  430.                wishbook[ctr].name,
  431.                uic[ctr],PLAYER.eud[ctr],PLAYER.ulc[ctr]);
  432.          plot_text(12,108+ctr*12,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  433.       OD
  434.  
  435.       // draw the black frame around this area
  436.       SetAPen(rast_port,BLACK);
  437.       Move(rast_port,107,81);
  438.       Draw(rast_port,530,81);
  439.       Draw(rast_port,530,239);
  440.       Draw(rast_port,107,239);
  441.       Draw(rast_port,107,81);
  442.       Move(rast_port,220,81);    Draw(rast_port,220,239);
  443.       Move(rast_port,316,81);    Draw(rast_port,316,239);
  444.       Move(rast_port,378,81);    Draw(rast_port,378,239);
  445.       Move(rast_port,467,81);    Draw(rast_port,467,239);
  446.    }
  447.  
  448.    {  // handle the user actions here
  449.       struct IntuiMessage *message; // the message the IDCMP sends us
  450.  
  451.       // useful for interpreting IDCMP messages
  452.       UWORD code;
  453.       ULONG class;
  454.       APTR object;
  455.       UWORD qualifier;
  456.  
  457.       FOREVER {
  458.          WaitPort(status_window->UserPort);
  459.          while (message = GT_GetIMsg(status_window->UserPort)) {
  460.             code = message->Code;  // MENUNUM
  461.             object = message->IAddress;  // Gadget
  462.             class = message->Class;
  463.             qualifier = message->Qualifier;
  464.             GT_ReplyIMsg(message);
  465.             if (class==IDCMP_VANILLAKEY) {
  466.                switch ((char)code) {
  467.                   // left-Amiga V or RETURN are defaults for "Okay"
  468.                   case 'v':
  469.                      if ((qualifier & IEQUALIFIER_LCOMMAND)==0)
  470.                         break;
  471.                   case 13:    // RETURN/ENTER
  472.                      // show the button depressed
  473.                      show_depress(okay_gad,status_window->RPort);
  474.                      Delay(10L);
  475.                      goto exit_status_window;
  476.                }
  477.             FI
  478.             if (class==IDCMP_GADGETUP)
  479.                if (object==okay_gad)
  480.                   goto exit_status_window;
  481.          OD
  482.       OD
  483.    }
  484.  
  485.  exit_status_window:
  486.    // now close up everything with the status_window
  487.    CloseWindow(status_window);
  488.    FreeGadgets(context);
  489.  
  490.    // reset everything for map window
  491.    rast_port = map_window->RPort;
  492.    ClearPointer(map_window);
  493.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  494. }
  495.  
  496. void end_of_player(player)
  497. int player;
  498. {
  499.    struct Window *eop_window = NULL;
  500.    struct Gadget *context, *okay_gad;
  501.    int wd=543, ht=246;
  502.    int left_edge, top_edge;
  503.    int music;
  504.    
  505.    struct NewGadget button = {
  506.       460,21,  // leftedge, topedge
  507.       66,16,   // width, height
  508.       "Okay",  // text label
  509.       NULL,    // font
  510.       1,       // gadget ID
  511.       NULL,NULL,NULL
  512.    };
  513.  
  514.    // make sure user doesn't play with the map window now
  515.    SetPointer(map_window,BUSY_POINTER);
  516.    ModifyIDCMP(map_window,NULL);
  517.  
  518.    if (!CreateContext(&context))
  519.       clean_exit(1,"Unable to create context gadget!");
  520.    button.ng_VisualInfo = vi;
  521.    button.ng_TextAttr = &topaz11bold;
  522.    okay_gad = CreateGadget(BUTTON_KIND,context,&button,TAG_END);
  523.  
  524.    left_edge = (map_screen->Width-wd)/2;
  525.    top_edge = (map_screen->Height-ht)/2;
  526.  
  527.    eop_window = OpenWindowTags(NULL,
  528.       WA_Gadgets,       context,
  529.       WA_Title,         "End Of Player Report - You're DEAD!",
  530.       WA_CustomScreen,  map_screen,
  531.       WA_Top,           top_edge,
  532.       WA_Left,          left_edge,
  533.       WA_Height,        ht,
  534.       WA_Width,         wd,
  535.       WA_IDCMP,         IDCMP_GADGETUP|IDCMP_VANILLAKEY,
  536.       WA_Flags,         WFLG_SMART_REFRESH|WFLG_DRAGBAR|WFLG_ACTIVATE,
  537.       TAG_END );
  538.    if (eop_window==NULL)
  539.       clean_exit(1,"ERROR: Unable to open eop report window!");
  540.    GT_RefreshWindow(eop_window,NULL);
  541.    rast_port = eop_window->RPort;
  542.  
  543.    // End Of Game tunes
  544.    music = GetPlayer(0);
  545.    if (music != 0)
  546.        clean_exit(1,"ERROR: Unable to open music player!");
  547.         
  548.    PlayModule(LoadModule("PROGDIR:Data/Music/afterwar.med"));
  549.    //SetTempo(33);     
  550.  
  551.    // this section is where I calculate and print the report info
  552.    {
  553.       struct City *metro;
  554.       struct Unit *unit;
  555.       int ctr, x, y, explored, all_cities=0, my_cities=0,
  556.          uuc[11],    // units under construction
  557.          usc[11],    // units soonest completion
  558.          uic[11];    // unit in combat
  559.  
  560.       for (ctr=0; ctr<11; ctr++) {
  561.          uuc[ctr] = 0;
  562.          usc[ctr] = -1;
  563.          uic[ctr] = 0;
  564.       }
  565.  
  566.       metro = (struct City *)city_list.mlh_Head;
  567.       for (; metro->cnode.mln_Succ; metro=(struct City *)metro->cnode.mln_Succ) {
  568.          all_cities++;
  569.          if (metro->owner==player) {
  570.             int industry, prod_time, utype=metro->unit_type;
  571.             my_cities++;
  572.             uuc[utype]++;
  573.             industry = (PLAYER.prod*metro->industry)/50;
  574.             prod_time = wishbook[utype].build/industry-metro->unit_wip/industry;
  575.             if (prod_time<usc[utype] || usc[utype]<0)
  576.                usc[utype] = prod_time;
  577.          }
  578.       }
  579.  
  580.       unit =  (struct Unit *)unit_list.mlh_Head;
  581.       for (; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ)
  582.          if (unit->owner==player)
  583.             uic[unit->type]++;
  584.  
  585.       explored = 0;
  586.       for (x=0; x<width; x++)
  587.          for (y=0; y<height; y++)
  588.             if (get(PLAYER.map,x,y)!=HEX_UNEXPLORED)
  589.                explored++;
  590.  
  591.       // print some text
  592.       sprintf(foo,"Turns %ld    Player #%ld    %s",turn,player,PLAYER.name);
  593.       plot_text(11,18,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  594.       plot_text(11,36,
  595.          "                                   World",
  596.          BLACK,LT_GRAY,JAM2,&topaz11);
  597.       plot_text(11,48,
  598.          "         Captured   Exist         Explored",
  599.          BLACK,LT_GRAY,JAM2,&topaz11);
  600.       sprintf(foo,"CITIES    %3ld       %3ld            %3ld",
  601.          my_cities, all_cities, (explored*100)/(width*height));
  602.       strcat(foo,"%");
  603.       plot_text(11,60,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  604.  
  605.       plot_text(116,84,"      We          We",
  606.          BLACK,LT_GRAY,JAM2,&topaz11); 
  607.       plot_text(116,96,"  Destroyed      Lost",
  608.          BLACK,LT_GRAY,JAM2,&topaz11);
  609.  
  610.       for (ctr=0;ctr<11;ctr++) {
  611.          if (usc[ctr]>=0)
  612.             sprintf(foo,"%-11s     %3ld          %3ld",
  613.                wishbook[ctr].name,PLAYER.eud[ctr],PLAYER.ulc[ctr]);
  614.          else
  615.             sprintf(foo,"%-11s     %3ld          %3ld",
  616.                wishbook[ctr].name,PLAYER.eud[ctr],PLAYER.ulc[ctr]);
  617.          if (wishbook[ctr].enabled==FALSE)
  618.             sprintf(foo,"%-11s     %3ld          %3ld",
  619.                wishbook[ctr].name,PLAYER.eud[ctr],PLAYER.ulc[ctr]);
  620.          plot_text(12,108+ctr*12,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  621.       OD
  622.  
  623.       // draw the black frame around this area
  624.       SetAPen(rast_port,BLACK);
  625.       Move(rast_port,107,81);
  626.       Draw(rast_port,530,81);
  627.       Draw(rast_port,530,239);
  628.       Draw(rast_port,107,239);
  629.       Draw(rast_port,107,81);
  630.       Move(rast_port,220,81);    Draw(rast_port,220,239);
  631.       Move(rast_port,316,81);    Draw(rast_port,316,239);
  632.       Move(rast_port,378,81);    Draw(rast_port,378,239);
  633.       Move(rast_port,467,81);    Draw(rast_port,467,239);
  634.    }
  635.  
  636.    {  // handle the user actions here
  637.       struct IntuiMessage *message; // the message the IDCMP sends us
  638.  
  639.       // useful for interpreting IDCMP messages
  640.       UWORD code;
  641.       ULONG class;
  642.       APTR object;
  643.       UWORD qualifier;
  644.  
  645.       FOREVER {
  646.          WaitPort(eop_window->UserPort);
  647.          while (message = GT_GetIMsg(eop_window->UserPort)) {
  648.             code = message->Code;  // MENUNUM
  649.             object = message->IAddress;  // Gadget
  650.             class = message->Class;
  651.             qualifier = message->Qualifier;
  652.             GT_ReplyIMsg(message);
  653.             if (class==IDCMP_VANILLAKEY) {
  654.                switch ((char)code) {
  655.                   // left-Amiga V or RETURN are defaults for "Okay"
  656.                   case 'v':
  657.                      if ((qualifier & IEQUALIFIER_LCOMMAND)==0)
  658.                         break;
  659.                   case 13:    // RETURN/ENTER
  660.                      // show the button depressed
  661.                      show_depress(okay_gad,eop_window->RPort);
  662.                      Delay(10L);
  663.                      goto exit_eop_window;
  664.                }
  665.             FI
  666.             if (class==IDCMP_GADGETUP)
  667.                if (object==okay_gad)
  668.                   goto exit_eop_window;
  669.          OD
  670.       OD
  671.    }
  672.  
  673.  exit_eop_window:
  674.    // now close up everything with the status_window
  675.    CloseWindow(eop_window);
  676.    FreeGadgets(context);
  677.    UnLoadModule(0);
  678.    FreePlayer();
  679.  
  680.    // reset everything for map window
  681.    rast_port = map_window->RPort;
  682.    ClearPointer(map_window);
  683.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  684. }
  685.  
  686.  
  687. /*
  688.    context_sound() is used to decide when and how loudly sound effects
  689.    should be played, based on the player's status.  I put it here instead
  690.    of in sound.c so it's easier to access all my defined values.  So sue me!
  691. */
  692.  
  693. void context_sound(snum)
  694. int snum;
  695. {
  696.    int volume=PLAYER.snd_vol;
  697.  
  698.    // I am going to decide whether the sound is played by a culling process
  699.    if (PLAYER.show&SHOW_SND==0)     // this bit enables the sounds for a player
  700.       return;
  701.    if (PLAYER.soundfx==SOUND_NONE || volume==0)
  702.       return;     // the player has turned his sounds off
  703.    if (PLAYER.soundfx==SOUND_BATTLE)
  704.       return;     // he only wants to hear the battles
  705.    /* now there's nothing left for it but to play the sound */
  706.    playSound(snum,volume);
  707. }
  708.  
  709.  
  710.  
  711. // Here's a function where I tell the player news of some game event.
  712. // Each player has his own time delay setting for messages.
  713.  
  714. void tell_player(news)
  715. char *news;
  716. {
  717. // TLB v0.13
  718.    SetWindowTitles(map_window,news,(UBYTE *)~0);
  719.    prop_delay(25,player,FALSE);
  720. //   strcpy(win_title,"Game in progress...");
  721.    SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  722. }
  723.  
  724.                
  725. /*
  726.    The player can call up a dialog box to set all his game preferences,
  727.    including sound, battle and message delays, show fighter & army
  728.    construction, automatic battle reports, and anything else I think of.
  729. */
  730. void player_preferences()
  731. {
  732.    struct Window *prefs_window = NULL;
  733.    struct Gadget *context, *okay_gad, *cancel_gad;
  734.    struct Gadget *btl_delay_slide, *msg_delay_slide, *snd_vol_slide;
  735.    struct Gadget *autodsp_check, *autorpt_check;
  736.    struct Gadget *soundfx_rollo;
  737.    int wd=509, ht=144;
  738.    int left_edge, top_edge;
  739.    BOOL cancel=FALSE;
  740.    struct NewGadget button = {
  741.       75,232,  // leftedge, topedge
  742.       66,16,   // width, height
  743.       "Okay",  // text label
  744.       NULL,    // font
  745.       1,       // gadget ID
  746.       NULL,NULL,NULL
  747.    };
  748.    struct NewGadget slider = {
  749.       11,33,
  750.       232,13,
  751.       NULL,
  752.       &topaz11,
  753.       5,
  754.       NULL,
  755.       NULL,NULL
  756.    };
  757.    struct NewGadget check = {
  758.       11,54,
  759.       26,11,
  760.       "Display non-ship unit construction?",
  761.       &topaz11,
  762.       8,
  763.       PLACETEXT_RIGHT,
  764.       NULL,NULL
  765.    };
  766.    struct NewGadget rollo = {
  767.       11,96, // leftedge, topedge
  768.       130,14, // width, height
  769.       "Sound Effects",     // text label
  770.       &topaz11,   // font
  771.       2,          // gadget ID
  772.       PLACETEXT_RIGHT,
  773.       NULL,NULL      // visual info, user data
  774.    };
  775.  
  776.    int new_msg_delay=PLAYER.msg_delay, new_battle_delay=PLAYER.battle_delay;
  777.    int new_snd_vol=PLAYER.snd_vol;
  778.    enum SoundFX new_soundfx=PLAYER.soundfx;
  779.  
  780.    // make sure he can't turn on the sound if the sound channels
  781.    // weren't successfully opened!
  782.    //if (device_open==FALSE) {
  783.    //   new_soundfx = SOUND_NONE;
  784.    //   new_snd_vol = 0;
  785.    //}
  786.  
  787.    // make sure user doesn't play with the map window now
  788.    SetPointer(map_window,BUSY_POINTER);
  789.    ModifyIDCMP(map_window,NULL);
  790.  
  791.    if (!CreateContext(&context))
  792.       clean_exit(1,"Unable to create context gadget!");
  793.    button.ng_VisualInfo = vi;
  794.    button.ng_TextAttr = &topaz11bold;
  795.    button.ng_TopEdge = ht-24;
  796.    okay_gad = CreateGadget(BUTTON_KIND,context,&button,TAG_END);
  797.    button.ng_LeftEdge += 250;
  798.    button.ng_TextAttr = &topaz11;
  799.    button.ng_GadgetText = "Cancel";
  800.    cancel_gad = CreateGadget(BUTTON_KIND,okay_gad,&button,TAG_END);
  801.    
  802.    // create the slider gadgets
  803.    slider.ng_VisualInfo = vi;
  804.    btl_delay_slide = CreateGadget(SLIDER_KIND,cancel_gad,&slider,
  805.       GTSL_Min,   1,
  806.       GTSL_Max,   100,
  807.       GTSL_Level, new_battle_delay,
  808.       GA_RelVerify, TRUE,
  809.       TAG_END);
  810.    slider.ng_LeftEdge = 266;
  811.    msg_delay_slide = CreateGadget(SLIDER_KIND,btl_delay_slide,&slider,
  812.       GTSL_Min,   1,
  813.       GTSL_Max,   100,
  814.       GTSL_Level, new_msg_delay,
  815.       GA_RelVerify, TRUE,
  816.       TAG_END);
  817.    slider.ng_TopEdge = 96;
  818.    snd_vol_slide = CreateGadget(SLIDER_KIND,msg_delay_slide,&slider,
  819.       GTSL_Min,   0,
  820.       GTSL_Max,   64,
  821.       GTSL_Level, new_snd_vol,
  822.       GA_Disabled,  FALSE,
  823.       GA_RelVerify, TRUE,
  824.       TAG_END);
  825.  
  826.    // create the checkmark gadgets
  827.    check.ng_VisualInfo = vi;
  828.    autodsp_check = CreateGadget(CHECKBOX_KIND,snd_vol_slide,&check,
  829.       GT_Underscore, '_',
  830.       GTCB_Checked, PLAYER.show_production,
  831.       TAG_END);
  832.    check.ng_TopEdge += 20;
  833.    check.ng_GadgetText = "Automatic combat reports?";
  834.    autorpt_check = CreateGadget(CHECKBOX_KIND,autodsp_check,&check,
  835.       GT_Underscore, '_',
  836.       GTCB_Checked, PLAYER.autorpt,
  837.       TAG_END);
  838.  
  839.    // create the rollo gadget
  840.    {
  841.       static char *soundfx_strings[] = {"All","Battle Only","None",NULL };
  842.       rollo.ng_VisualInfo = vi;
  843.       soundfx_rollo = CreateGadget(CYCLE_KIND,autorpt_check,&rollo,
  844.          GTCY_Labels, soundfx_strings,
  845.          GTCY_Active, (int)new_soundfx,
  846.          GA_Disabled,  FALSE,
  847.          TAG_END );
  848.    }
  849.  
  850.    left_edge = (map_screen->Width-wd)/2;
  851.    top_edge = (map_screen->Height-ht)/2;
  852.  
  853.    prefs_window = OpenWindowTags(NULL,
  854.       WA_Gadgets,       context,
  855.       WA_Title,         "Player Preferences",
  856.       WA_CustomScreen,  map_screen,
  857.       WA_Top,           top_edge,
  858.       WA_Left,          left_edge,
  859.       WA_Height,        ht,
  860.       WA_Width,         wd,
  861.       WA_IDCMP,         IDCMP_GADGETUP|IDCMP_VANILLAKEY,
  862.       WA_Flags,         WFLG_SMART_REFRESH|WFLG_DRAGBAR|WFLG_ACTIVATE,
  863.       TAG_END );
  864.    if (prefs_window==NULL)
  865.       clean_exit(1,"ERROR: Unable to open player prefs window!");
  866.    GT_RefreshWindow(prefs_window,NULL);
  867.    rast_port = prefs_window->RPort;
  868.  
  869.    plot_text(11,21,"Battle Delay",BLACK,LT_GRAY,JAM2,&topaz11);
  870.    plot_text(266,21,"Message Delay",BLACK,LT_GRAY,JAM2,&topaz11);
  871.    plot_text(266,84,"Sound Volume",BLACK,LT_GRAY,JAM2,&topaz11);
  872.  
  873.    {  // handle the user actions here
  874.       struct IntuiMessage *message; // the message the IDCMP sends us
  875.  
  876.       // useful for interpreting IDCMP messages
  877.       UWORD code;
  878.       ULONG class;
  879.       APTR object;
  880.       UWORD qualifier;
  881.  
  882.       FOREVER {
  883.          WaitPort(prefs_window->UserPort);
  884.          while (message = GT_GetIMsg(prefs_window->UserPort)) {
  885.             code = message->Code;  // MENUNUM
  886.             object = message->IAddress;  // Gadget
  887.             class = message->Class;
  888.             qualifier = message->Qualifier;
  889.             GT_ReplyIMsg(message);
  890.             if (class==IDCMP_VANILLAKEY) {
  891.                if (code==13) {
  892.                   // show the button depressed
  893.                   show_depress(okay_gad,prefs_window->RPort);
  894.                   Delay(10L);
  895.                   goto exit_prefs_window;
  896.                FI
  897.             FI
  898.             if (class==IDCMP_GADGETUP) {
  899.                if (object==okay_gad)
  900.                   goto exit_prefs_window;
  901.                if (object==cancel_gad) {
  902.                   cancel = TRUE;
  903.                   goto exit_prefs_window;
  904.                FI
  905.                if (object==btl_delay_slide)
  906.                   new_battle_delay = code;
  907.                if (object==msg_delay_slide)
  908.                   new_msg_delay = code;
  909.                if (object==snd_vol_slide)
  910.                   new_snd_vol = code;
  911.                if (object==soundfx_rollo)
  912.                   new_soundfx = (enum SoundFX)code;
  913.             FI
  914.          OD
  915.       OD
  916.    }
  917.  
  918.  exit_prefs_window:
  919.    // if the user didn't cancel, copy all the new values
  920.    if (cancel==FALSE) {
  921.       PLAYER.msg_delay        = new_msg_delay;
  922.       PLAYER.battle_delay     = new_battle_delay;
  923.       PLAYER.soundfx          = new_soundfx;
  924.       PLAYER.snd_vol          = new_snd_vol;
  925.       PLAYER.autorpt          = (autorpt_check->Flags & GFLG_SELECTED)!=NULL;
  926.       PLAYER.show_production  = (autodsp_check->Flags & GFLG_SELECTED)!=NULL;
  927.    FI
  928.  
  929.    // now close up everything with the prefs_window
  930.    CloseWindow(prefs_window);
  931.    FreeGadgets(context);
  932.  
  933.    // reset everything for map window
  934.    rast_port = map_window->RPort;
  935.    ClearPointer(map_window);
  936.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  937. }
  938.  
  939.  
  940. /*
  941.    prop_delay() creates a delay for messages which is proportional based
  942.    on the current player's pref settings and the type of message (battle
  943.    or other) using a provided base value (length)
  944. */
  945.  
  946. void prop_delay(length,player,bflag)
  947. int length, player;
  948. BOOL bflag;
  949. {
  950.    long final;
  951.  
  952.    if (bflag)
  953.       final = length*PLAYER.battle_delay/50;
  954.    else
  955.       final = length*PLAYER.msg_delay/50;
  956.    Delay(final);
  957. }
  958.  
  959.  
  960. /*
  961.    tell the user about some event with an optional sound effect and
  962.    a delay controlled by battle_flag
  963. */
  964.  
  965. void tell_user(text,battle_flag,sound)
  966. char *text;
  967. BOOL battle_flag;
  968. int sound;
  969. {
  970.    SetWindowTitles(map_window,text,(UBYTE *)~0);
  971. //   if( (PLAYER.soundfx == SOUND_ALL) && (sound>=0) )
  972. //      playSound(sound,PLAYER.snd_vol);
  973.    prop_delay(100,player,battle_flag);
  974.    SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  975. }
  976.  
  977. void tell_user2(text,battle_flag,sound)
  978. char *text;
  979. BOOL battle_flag;
  980. int sound;
  981. {
  982.    SetWindowTitles(map_window,text,(UBYTE *)~0);
  983.    if( (PLAYER.soundfx == SOUND_ALL) )
  984.       playSound(sound,PLAYER.snd_vol);
  985.    prop_delay(100,player,battle_flag);
  986.    SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  987. }
  988.  
  989. /*
  990.    show_battle() displays an individual battle action to the player with
  991.    graphics and sound -- the battle information is found in my usual "battle"
  992.    data structure -- no checking is done to determine whether this battle
  993.    should be visible to the current player, so don't call it unless you
  994.    already know
  995. */
  996. void show_battle()
  997. {
  998.    int xa=battle.att_x, ya=battle.att_y;  // for brevity
  999.    int xd=battle.def_x, yd=battle.def_y;
  1000.    int ctr, def_color;
  1001.    ULONG mask;
  1002.    char tbar[80];
  1003.  
  1004.    // show the individual battle to the player
  1005.    unsigned int num_blows, bit_blows;
  1006.  
  1007.    // store the window title for later
  1008.    strncpy(tbar,win_title,79L);
  1009.  
  1010.    // get the map to the right part of the display
  1011.    if (!display) {
  1012.       create_player_display(xd,yd);
  1013.    } else if (need_to_scrollP(xd,yd)) {
  1014.       int ox=xoffs, oy=yoffs;
  1015.  
  1016.       set_display_offsets(xd,yd);
  1017.       GP_smart_scroll(ox,oy);
  1018.    FI
  1019.  
  1020.    // save the background of both hexes (attacker & defender)
  1021.    save_hex_graphics(xa,ya,1);   // buffer 1 for attacker
  1022.    save_hex_graphics(xd,yd,2);  // buffer 2 for defender
  1023.  
  1024.    // redraw the hex background terrain just to make sure there are no
  1025.    // "edges" of other icons protruding where they shouldn't be -- only
  1026.    // the combatants should be visible
  1027.    plot_hex(xa,ya,get(t_grid,xa,ya));
  1028.    if (get_flags(t_grid,xa,ya)&ROAD)
  1029.       GP_draw_roads(xa,ya);
  1030.    plot_hex(xd,yd,get(t_grid,xd,yd));
  1031.    if (get_flags(t_grid,xd,yd)&ROAD)
  1032.       GP_draw_roads(xd,yd);
  1033.  
  1034.  
  1035.    /*
  1036.       If the defending unit is a militia, it's shown in white,
  1037.       no matter who it actually belongs to.
  1038.    */
  1039.    def_color = roster[battle.def_owner].color;
  1040.    if (battle.white_icon)
  1041.       def_color=WHITE;
  1042.  
  1043.    // draw the attacker and defender into their positions
  1044.    // for now I'll just draw right over whatever was there before
  1045.    plot_icon(battle.att_type,xa,ya,
  1046.       roster[battle.att_owner].color,0L,FALSE);
  1047.    plot_icon(battle.def_type,xd,yd,
  1048.       def_color,0L,FALSE);
  1049.  
  1050.    // set the title bar to explain what's happening
  1051.    if (battle.def_owner==player)
  1052.       sprintf(win_title,"%s's %s attacking your %s!",
  1053.          roster[battle.att_owner].name,
  1054.          wishbook[battle.att_type].name,
  1055.          wishbook[battle.def_type].name);
  1056.    else if (city_hereP(xd,yd))
  1057.       sprintf(win_title,"%s's %s attacking %s!",
  1058.          roster[battle.att_owner].name,
  1059.          wishbook[battle.att_type].name,
  1060.          city_hereP(xd,yd)->name);
  1061.    else
  1062.       sprintf(win_title,"%s's %s attacking %s's %s!",
  1063.          roster[battle.att_owner].name,
  1064.          wishbook[battle.att_type].name,
  1065.          roster[battle.def_owner].name,
  1066.          wishbook[battle.def_type].name);
  1067.    SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  1068.  
  1069.    prop_delay(45,player,TRUE);
  1070.  
  1071.    // draw the explosions for trading blows
  1072.    num_blows = (0xFF000000 & battle.blows)>>24;
  1073.    bit_blows = (0x00FFFFFF & battle.blows);
  1074.    mask = 1L<<(num_blows-1);
  1075.    for (ctr=0; ctr<num_blows; ctr++) {
  1076.       int target = bit_blows & mask;
  1077.       prop_delay(20,player,TRUE);
  1078.       mask >>= 1;    // move mask to next bit
  1079.       if (target!=0) { // attacker landed a blow on defender
  1080.          // draw explosion onto defender hex
  1081.          plot_mapobject(xd,yd,154,30);
  1082.          // wait time delay
  1083.          context_sound(BOOM_SOUND);
  1084.          prop_delay(35,player,TRUE);
  1085.          // erase explosion (i.e. redraw defender)
  1086.          plot_icon(battle.def_type,xd,yd,
  1087.             def_color,0L,FALSE);
  1088.       } else {
  1089.          // draw explosion onto attacker hex
  1090.          plot_mapobject(xa,ya,154,30);
  1091.          // wait time delay
  1092.          context_sound(BOOM_SOUND);
  1093.          prop_delay(35,player,TRUE);
  1094.          // erase explosion (i.e. redraw attacker)
  1095.          plot_icon(battle.att_type,xa,ya,
  1096.             roster[battle.att_owner].color,0L,FALSE);
  1097.       FI
  1098.    OD
  1099.    // play an appropriate sound effect
  1100.    if (battle.winner==player)
  1101.       context_sound(YEAH_SOUND);
  1102.    else
  1103.       context_sound(DIE_SOUND);
  1104.  
  1105.    if (battle.winner==battle.att_owner && battle.bombardment==FALSE) {
  1106.       struct Unit flarp;
  1107.  
  1108.       flarp.owner = battle.att_owner;
  1109.       flarp.type = battle.att_type;
  1110.       anim_move(&flarp,xa,ya,xd,yd);
  1111.       GP_update_at_hex(xd,yd);
  1112.    } else
  1113.       Delay(15L);
  1114.  
  1115.    // restore the original graphics for this hex
  1116.    restore_hex_graphics(xa,ya,1);
  1117.    restore_hex_graphics(xd,yd,2);
  1118.    strcpy(win_title,tbar);  // restore window title to former value
  1119.    SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  1120. }
  1121.  
  1122.  
  1123. void show_combat_report(avto)
  1124. BOOL avto;
  1125. {  // show the current player his combat report
  1126.    BPTR infile;
  1127.    ULONG length;
  1128.    int ctr, incidence=0;
  1129.    char tbar[80];
  1130.  
  1131.    strcpy(foo,prefix);
  1132.    strcat(foo,id_filetag);
  1133.    strcat(foo,".CR");
  1134.    if ((length=FLength(foo))<=0)
  1135.       goto no_report;
  1136.    if (length>=sizeof(battle))
  1137.       infile = Open(foo,MODE_OLDFILE);
  1138.    if (!infile)
  1139.       return;  // add error alert here later
  1140.    strncpy(tbar,win_title,79L);  // store the window title for later
  1141.    for (ctr=0; ctr<(length/sizeof(battle)); ctr++) {
  1142.       Read(infile,&battle,sizeof(battle));
  1143.       if (battle.seen_by & mask(player)) {
  1144.          show_battle();
  1145.          incidence++;
  1146.       FI
  1147.    OD
  1148.    Close(infile);
  1149.    strcpy(win_title,tbar);  // restore window title to former value
  1150.    SetWindowTitles(map_window,win_title,(UBYTE *)~0);
  1151.  no_report:
  1152.    // we never throw this requester if the report is done avtomatically
  1153.    if (incidence==0 && avto==FALSE)
  1154.       (void)rtEZRequestTags("There are no battles to report!","Okay",
  1155.          NULL,NULL,
  1156.          RT_Window,        map_window,
  1157.          RT_ReqPos,        REQPOS_CENTERSCR,
  1158.          RT_LockWindow,    TRUE,
  1159.          RT_ShareIDCMP,    TRUE,
  1160.          RTEZ_Flags,       EZREQF_CENTERTEXT,
  1161.          TAG_END);
  1162. }
  1163.  
  1164.  
  1165. // display a global world view of the terrain
  1166.  
  1167. void ME_world_view()
  1168. {
  1169.    int x, y;    // loop counters
  1170.    int fatness = 6;   // fat pixel size
  1171.    int wv_height, wv_width, wv_left, wv_top;
  1172.    struct Window *wv_window;
  1173.  
  1174.    // make sure user doesn't play with the map window now
  1175.    SetPointer(map_window,BUSY_POINTER);
  1176.    ModifyIDCMP(map_window,NULL);
  1177.  
  1178.    // pixelly math to size and center the window
  1179.    while (TRUE) {
  1180.       wv_height = height*fatness+16;
  1181.       wv_width = width*fatness+fatness/2+8;
  1182.       if (wv_height>map_screen->Height || wv_width>map_screen->Width)
  1183.          fatness -= 2;
  1184.       else
  1185.          break;
  1186.    }
  1187.    wv_left = (map_screen->Width-wv_width)/2;
  1188.    wv_top = (map_screen->Height-wv_height)/2;
  1189.  
  1190.    wv_window = OpenWindowTags(NULL,
  1191.       WA_CustomScreen, map_screen,
  1192.       WA_Width,   wv_width,
  1193.       WA_Height,  wv_height,
  1194.       WA_Left,    wv_left,
  1195.       WA_Top,     wv_top,
  1196.       WA_Title, "World View",
  1197.       WA_IDCMP, IDCMP_CLOSEWINDOW,
  1198.       WA_Flags, WFLG_ACTIVATE|NOCAREREFRESH|WFLG_CLOSEGADGET|WFLG_DRAGBAR,
  1199.       TAG_END);
  1200.    if (wv_window==NULL) {
  1201.       DisplayBeep(NULL);
  1202.       ClearPointer(map_window);
  1203.       ModifyIDCMP(map_window,IDCMP_MAPEDIT);
  1204.       return;
  1205.    FI
  1206.    rast_port = wv_window->RPort;
  1207.  
  1208.    for (y = 0; y<height; y++) {
  1209.       int indent = (y%2)*(fatness/2);
  1210.       for (x = 0; x<width; x++) {
  1211.          int terra = get(t_grid,x,y);
  1212.          int color;
  1213.  
  1214.          // here I translate my hexes to the appropriate colors
  1215.          switch (terra) {
  1216.             case HEX_PLAINS:
  1217.             case HEX_BRUSH:
  1218.                color = LT_GREEN;
  1219.                break;
  1220.             case HEX_FOREST:
  1221.                color = GREEN;
  1222.                break;
  1223.             case HEX_JUNGLE:
  1224.                color = DK_GREEN;
  1225.                break;
  1226.             case HEX_SWAMP:
  1227.                color = PURPLE;
  1228.                break;
  1229.             case HEX_ICE:
  1230.                color = WHITE;
  1231.                break;
  1232.             case HEX_DESERT:
  1233.                color = TAN;
  1234.                break;
  1235.             case HEX_RUGGED:
  1236.                color = BROWN;
  1237.                break;
  1238.             case HEX_HILLS:
  1239.                color = GRAY;
  1240.                break;
  1241.             case HEX_MOUNTAINS:
  1242.                color = DK_GRAY;
  1243.                break;
  1244.             case HEX_PEAKS:
  1245.             case HEX_FORBID:
  1246.                color = BLACK;
  1247.                break;
  1248.             case HEX_SHALLOWS:
  1249.                color = LT_BLUE;
  1250.                break;
  1251.             case HEX_OCEAN:
  1252.                color = BLUE;
  1253.                break;
  1254.             case HEX_DEPTH:
  1255.                color = DK_BLUE;
  1256.                break;
  1257.          }
  1258.          SetAPen(wv_window->RPort,color);
  1259.          fat_plot(wv_window->RPort,x*fatness+indent+4,y*fatness+14,fatness);
  1260.       OD
  1261.    OD
  1262.  
  1263.    // display the cities
  1264.    {
  1265.       struct City *metro = (struct City *)city_list.mlh_Head;
  1266.       SetAPen(wv_window->RPort,ORANGE);
  1267.       for ( ; metro->cnode.mln_Succ; metro = (struct City *)metro->cnode.mln_Succ) {
  1268.          int col = metro->col, row = metro->row;
  1269.          int indent = (row%2)*(fatness/2);
  1270.          fat_plot(wv_window->RPort,col*fatness+indent+5,row*fatness+15,fatness-2);
  1271.       OD
  1272.    }
  1273.  
  1274.    // draw the frame showing the current main window position
  1275.    {
  1276.       int x, y, w, h;
  1277.  
  1278.       SetDrMd(rast_port,COMPLEMENT);
  1279.       x = xoffs*fatness+5;
  1280.       w = disp_wd*fatness;
  1281.       y = yoffs*fatness+14;
  1282.       h = disp_ht*fatness;
  1283.       box(x,y,w,h,BLACK);
  1284.    }
  1285.  
  1286.    {  // wait for the close window message
  1287.       struct IntuiMessage *message; // the message the IDCMP sends us
  1288.       ULONG class;
  1289.  
  1290.       while (TRUE) {
  1291.          WaitPort(wv_window->UserPort);
  1292.          while (message = GT_GetIMsg(wv_window->UserPort)) {
  1293.             class = message->Class;
  1294.             GT_ReplyIMsg(message);
  1295.             if (class==IDCMP_CLOSEWINDOW)
  1296.                goto exit_worldview;
  1297.          OD
  1298.       OD
  1299.    }
  1300.  exit_worldview:
  1301.    rast_port = map_window->RPort;
  1302.    CloseWindow(wv_window);
  1303.    ClearPointer(map_window);
  1304.    ModifyIDCMP(map_window,IDCMP_MAPEDIT);
  1305. }
  1306.  
  1307.  
  1308. void GP_world_view()
  1309. {
  1310.    int x, y;    // loop counters
  1311.    int fatness = 6;   // fat pixel size
  1312.    int wv_height, wv_width, wv_left, wv_top;
  1313.    struct Window *wv_window;
  1314.  
  1315.    // make sure user doesn't play with the map window now
  1316.    SetPointer(map_window,BUSY_POINTER);
  1317.    ModifyIDCMP(map_window,NULL);
  1318.  
  1319.    // pixelly math to size and center the window
  1320.    while (TRUE) {
  1321.       wv_height = height*fatness+16;
  1322.       wv_width = width*fatness+fatness/2+8;
  1323.       if (wv_height>map_screen->Height || wv_width>map_screen->Width)
  1324.          fatness -= 2;
  1325.       else
  1326.          break;
  1327.    }
  1328.    wv_left = (map_screen->Width-wv_width)/2;
  1329.    wv_top = (map_screen->Height-wv_height)/2;
  1330.  
  1331.    wv_window = OpenWindowTags(NULL,
  1332.       WA_CustomScreen, map_screen,
  1333.       WA_Width,   wv_width,
  1334.       WA_Height,  wv_height,
  1335.       WA_Left,    wv_left,
  1336.       WA_Top,     wv_top,
  1337.       WA_Title,   "World View",
  1338.       WA_IDCMP,   IDCMP_CLOSEWINDOW,
  1339.       WA_Flags,   WFLG_ACTIVATE|NOCAREREFRESH|WFLG_CLOSEGADGET|WFLG_DRAGBAR,
  1340.       TAG_END);
  1341.    if (wv_window==NULL) {
  1342.       DisplayBeep(NULL);
  1343.       ClearPointer(map_window);
  1344.       ModifyIDCMP(map_window,IDCMP_MAPEDIT);
  1345.       return;
  1346.    FI
  1347.    rast_port = wv_window->RPort;
  1348.  
  1349.    for (y = 0; y<height; y++) {
  1350.       int indent = (y%2)*(fatness/2);
  1351.       for (x = 0; x<width; x++) {
  1352.          int terra = get(PLAYER.map,x,y);
  1353.          int color;
  1354.  
  1355.          // Here I translate my hexes to the appropriate colors.
  1356.          // I must use as few colors as possible, so I will have some
  1357.          // left for the units.  In practice, that means one color for
  1358.          // water and another for land.
  1359.          color = BLACK;
  1360.          switch (terra) {
  1361.             case HEX_PLAINS:
  1362.             case HEX_BRUSH:
  1363.             case HEX_FOREST:
  1364.             case HEX_JUNGLE:
  1365.             case HEX_SWAMP:
  1366.             case HEX_ICE:
  1367.             case HEX_DESERT:
  1368.             case HEX_RUGGED:
  1369.             case HEX_HILLS:
  1370.             case HEX_MOUNTAINS:
  1371.                color = DK_GREEN;
  1372.                break;
  1373.             case HEX_UNEXPLORED:
  1374.                color = DK_GRAY;
  1375.                break;
  1376.             case HEX_PEAKS:
  1377.             case HEX_FORBID:
  1378.                color = BLACK;
  1379.                break;
  1380.             case HEX_SHALLOWS:
  1381.             case HEX_OCEAN:
  1382.             case HEX_DEPTH:
  1383.                color = DK_BLUE;
  1384.          }
  1385.          SetAPen(wv_window->RPort,color);
  1386.          fat_plot(wv_window->RPort,x*fatness+indent+4,y*fatness+14,fatness);
  1387.       OD
  1388.    OD
  1389.  
  1390.    // display the icons
  1391.    {
  1392.       struct MapIcon *piece=(struct MapIcon *)PLAYER.icons.mlh_Head;
  1393.  
  1394.       for (; piece->inode.mln_Succ; piece=(struct MapIcon *)piece->inode.mln_Succ) {
  1395.          int col=piece->col, row=piece->row;
  1396.          int indent = (row%2)*(fatness/2);
  1397.          SetAPen(wv_window->RPort,roster[piece->owner].color);
  1398.          fat_plot(wv_window->RPort,col*fatness+indent+5,row*fatness+15,fatness-2);
  1399.       OD
  1400.    }
  1401.  
  1402.    // draw the frame showing the current main window position
  1403.    {
  1404.       int x, y, w, h;
  1405.  
  1406.       SetDrMd(rast_port,COMPLEMENT);
  1407.       x = xoffs*fatness+5;
  1408.       w = disp_wd*fatness;
  1409.       y = yoffs*fatness+14;
  1410.       h = disp_ht*fatness;
  1411.       box(x,y,w,h,BLACK);
  1412.    }
  1413.  
  1414.    {  // wait for the close window message
  1415.       struct IntuiMessage *message; // the message the IDCMP sends us
  1416.       ULONG class;
  1417.  
  1418.       while (TRUE) {
  1419.          WaitPort(wv_window->UserPort);
  1420.          while (message = GT_GetIMsg(wv_window->UserPort)) {
  1421.             class = message->Class;
  1422.             GT_ReplyIMsg(message);
  1423.             if (class==IDCMP_CLOSEWINDOW)
  1424.                goto exit_worldview;
  1425.          OD
  1426.       OD
  1427.    }
  1428.  exit_worldview:
  1429.    rast_port = map_window->RPort;
  1430.    CloseWindow(wv_window);
  1431.    ClearPointer(map_window);
  1432.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  1433. }
  1434.  
  1435.  
  1436. /*
  1437.    sector_survey() is where I give the player everything he could possibly
  1438.    want to know about a given hex on his map; this likely means that he was
  1439.    in survey mode and asked for hex info, but could possibly come up under
  1440.    other conditions as well
  1441. */
  1442.  
  1443. void sector_survey(col,row)
  1444. int col, row;
  1445. {
  1446.    struct Window *ss_window;
  1447.    int ss_height, ss_width, ss_left, ss_top;
  1448.    int terrain_type;
  1449.    struct MapIcon *city_icon=NULL;
  1450.  
  1451.    struct Gadget *context, *cont_gad, *city_gad, *units_gad;
  1452.    struct NewGadget button = {
  1453.       22,129,  // leftedge, topedge
  1454.       85,14,   // width, height
  1455.       "Continue",  // text label
  1456.       NULL,    // font
  1457.       1,       // gadget ID
  1458.       NULL,NULL,NULL
  1459.    };
  1460.  
  1461.    // make sure user doesn't play with the map window now
  1462.    SetPointer(map_window,BUSY_POINTER);
  1463.    ModifyIDCMP(map_window,NULL);
  1464.  
  1465. // TLB v0.13
  1466.    // do this so the user can see which city he is looking at
  1467.    save_hex_graphics(col,row,0);    // blit the background to a safe place
  1468.    plot_mapobject(col,row,MAP_MARKER);
  1469.  
  1470.  
  1471. // TLB v0.13
  1472.    // new pixelly math to size and position the window
  1473.    // it should be near, but not on top of, the specified hex
  1474.    {
  1475.       int mx, my;
  1476.       log_to_abs(col,row,&mx,&my);
  1477.  
  1478.       ss_height = 149;
  1479.       ss_width = 387;
  1480. //      ss_left = (map_screen->Width-ss_width)/2;
  1481.       ss_left = mx-10;
  1482.       if (row-yoffs<(disp_ht/2))
  1483.          ss_top = (my+46);
  1484.       else
  1485.          ss_top = (my-ss_height);
  1486.  
  1487. //      ss_top = (map_screen->Height-ss_height)/2;
  1488.    }
  1489.  
  1490.    // create gadgets
  1491.    if (!CreateContext(&context))
  1492.       clean_exit(1,"Unable to create context gadget!");
  1493.    button.ng_VisualInfo = vi;
  1494.    button.ng_TextAttr = &topaz11bold;
  1495.    cont_gad = CreateGadget(BUTTON_KIND,context,&button,TAG_END);
  1496.    button.ng_LeftEdge = 115;
  1497.    button.ng_Width = 134;
  1498.    button.ng_GadgetText = "City Production";
  1499.    button.ng_TextAttr = &topaz11;
  1500.    button.ng_Flags = NULL;
  1501.    {
  1502.       BOOL disabled=TRUE;
  1503.       struct City *nerble=city_hereP(col,row);
  1504.  
  1505.       if (nerble)
  1506.          if (nerble->owner==player)
  1507.             disabled = FALSE;
  1508.       city_gad = CreateGadget(BUTTON_KIND,cont_gad,&button,
  1509.          GA_Disabled, disabled, TAG_END);
  1510.    }
  1511.    button.ng_LeftEdge = 256;
  1512.    button.ng_Width = 105;
  1513.    button.ng_GadgetText = "Unit Survey";
  1514.    button.ng_TextAttr = &topaz11;
  1515.    button.ng_Flags = NULL;
  1516.    units_gad = CreateGadget(BUTTON_KIND,city_gad,&button,
  1517.       GA_Disabled, FALSE, TAG_END);
  1518.  
  1519.    ss_window = OpenWindowTags(NULL,
  1520.       WA_CustomScreen, map_screen,
  1521.       WA_Width,   ss_width,
  1522.       WA_Height,  ss_height,
  1523.       WA_Left,    ss_left,
  1524.       WA_Top,     ss_top,
  1525.       WA_Title,   "Sector Survey",
  1526.       WA_Gadgets, context,
  1527.       WA_IDCMP,   IDCMP_GADGETUP,
  1528.       WA_Flags,   WFLG_ACTIVATE|NOCAREREFRESH|WFLG_DRAGBAR,
  1529.       TAG_END);
  1530.    if (ss_window==NULL) {
  1531.       DisplayBeep(NULL);
  1532.       ClearPointer(map_window);
  1533.       ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  1534.       return;
  1535.    FI
  1536.    rast_port = ss_window->RPort;
  1537.  
  1538.    // Here's where all the info rendering will happen.
  1539.    bevel_box(9,61,368,3,FALSE);
  1540.    bevel_box(9,122,368,3,FALSE);
  1541.    terrain_type = get(PLAYER.map,col,row);
  1542.    SetAPen(rast_port,BLACK);
  1543.    px_outline_hex(9,22);
  1544.    px_plot_hex(9,22,terrain_type);
  1545.    plot_text(48,23,terrain_name_table[terrain_type],
  1546.       BLACK,LT_GRAY,JAM2,&topaz11);
  1547.    sprintf(foo,"ROW %ld",row);
  1548.    plot_text(168,23,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  1549.    sprintf(foo,"COLUMN %ld",col);
  1550.    plot_text(168,35,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  1551.    // Figure out who apparently controls the specified hex,
  1552.    // based on information from the map of the current player
  1553.    {
  1554.       struct MapIcon *icon;
  1555.       int controller = 0;
  1556.       for (icon=(struct MapIcon *)PLAYER.icons.mlh_Head; icon->inode.mln_Succ; icon=(struct MapIcon *)icon->inode.mln_Succ)
  1557.          if (icon->col==col && icon->row==row) {
  1558.             controller = icon->owner;
  1559.             if (icon->type==CITY)
  1560.                city_icon = icon;
  1561.          FI
  1562.       if (controller!=0 && terrain_type!=HEX_UNEXPLORED) {
  1563.          sprintf(foo,"Controlled by: %s",roster[controller].name);
  1564.          outline_text(48,47,foo,roster[controller].color,&topaz11);
  1565.       } else {
  1566.          if (controller==0)
  1567.             if (city_hereP(col,row))
  1568.                strcpy(foo,"Controlled by: NEUTRAL");
  1569.             else
  1570.                strcpy(foo,"Controlled by: NONE");
  1571.          if (terrain_type==HEX_UNEXPLORED)
  1572.             strcpy(foo,"Controlled by: UNKNOWN");
  1573.          plot_text(48,47,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  1574.       FI
  1575.    }
  1576.    // if there is a known city here, we do more info
  1577.    if (city_icon) {
  1578.       struct City *metro=city_hereP(col,row);
  1579.       // plot the icon picture of the city
  1580.       px_plot_city_complete(15,72,roster[city_icon->owner].color,FALSE);
  1581.       plot_text(48,70,metro->name,BLACK,LT_GRAY,JAM2,&topaz11);
  1582.       if (metro->owner==player) {
  1583.          px_plot_icon(metro->unit_type,14,98,roster[city_icon->owner].color,0,FALSE);
  1584.          sprintf(foo,"Producing %s",wishbook[metro->unit_type].name);
  1585.          plot_text(48,103,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  1586.       } else {
  1587.          if (metro->recon[player]!=CITY) {
  1588.             px_plot_icon(metro->recon[player],14,98,roster[city_icon->owner].color,0,FALSE);
  1589.             sprintf(foo,"Producing %s",wishbook[metro->recon[player]].name);
  1590.             plot_text(48,103,foo,BLACK,LT_GRAY,JAM2,&topaz11);
  1591.          } else {
  1592.             bevel_box(14,98,20,16,FALSE);
  1593.             plot_text(48,103,"Production Unknown",BLACK,LT_GRAY,JAM2,&topaz11);
  1594.          FI
  1595.       FI
  1596.    FI
  1597.  
  1598.    // Here is the familiar user input loop...
  1599.    {
  1600.       struct IntuiMessage *message; // the message the IDCMP sends us
  1601.  
  1602.       // useful for interpreting IDCMP messages
  1603.       UWORD code;
  1604.       ULONG class;
  1605.       APTR object;
  1606.       UWORD qualifier;
  1607.  
  1608.       FOREVER {
  1609.          WaitPort(ss_window->UserPort);
  1610.          while (message = GT_GetIMsg(ss_window->UserPort)) {
  1611.             code = message->Code;  // MENUNUM
  1612.             object = message->IAddress;  // Gadget
  1613.             class = message->Class;
  1614.             qualifier = message->Qualifier;
  1615.             GT_ReplyIMsg(message);
  1616.             if (class==IDCMP_GADGETUP) {
  1617.                if (object==cont_gad)
  1618.                   goto exit_sector_survey;
  1619.                if (object==units_gad) {
  1620.                   unit_survey(col,row);
  1621.                   break;
  1622.                FI
  1623.                if (object==city_gad) {
  1624.                   struct City *nerble=city_hereP(col,row);
  1625.                   if (nerble)
  1626.                      if (nerble->owner==player) {
  1627.                         // first disable the window
  1628.                         SetPointer(ss_window,BUSY_POINTER);
  1629.                         ModifyIDCMP(ss_window,NULL);
  1630.                         examine_city(nerble);
  1631.                         // then re-enable the windows
  1632.                         ClearPointer(ss_window);
  1633.                         ModifyIDCMP(ss_window,IDCMP_GADGETUP);
  1634.                      FI
  1635.                FI
  1636.             FI
  1637.          OD
  1638.       OD
  1639.    }
  1640.  
  1641.  exit_sector_survey:
  1642.    rast_port = map_window->RPort;
  1643.    CloseWindow(ss_window);
  1644.    FreeGadgets(context);
  1645.  
  1646. // TLB v0.13
  1647.    // clear the sector marker from the map display
  1648.    restore_hex_graphics(col,row,0);
  1649.  
  1650.    ClearPointer(map_window);
  1651.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  1652. }
  1653.  
  1654.  
  1655. /*
  1656.    unit_survey() is supposed to provide a survey of the units currently in a
  1657.    given hex sector.  This is supposedly called when the user has asked for
  1658.    the info on a hex he controls, but the same techniques could be applied
  1659.    to generate other kinds of reports.  Our goal is to create a temporary
  1660.    text file and then call MultiView to display it.
  1661. */
  1662.  
  1663. void unit_survey(col,row)
  1664. int col, row;
  1665. {
  1666.    struct Unit *unit=(struct Unit *)unit_list.mlh_Head;
  1667.    char *filepath="t:unit_report.txt";
  1668.    char foo[128], bar[40];
  1669.    BPTR outfile;
  1670.  
  1671.    /* open the temporary file */
  1672.    outfile = Open(filepath,MODE_NEWFILE);
  1673.    if (outfile==NULL) return;
  1674.  
  1675.    for (; unit->unode.mln_Succ; unit = (struct Unit *)unit->unode.mln_Succ)
  1676.       if (unit->col==col && unit->row==row) {
  1677.          sprintf(foo,"%s \"%s\"",wishbook[unit->type].name,unit->name);
  1678.          if (unit->cargo>0) {
  1679.             /* I have to generate a string showing what kind of cargo is on board. */
  1680.             struct Unit *cargo=(struct Unit *)unit_list.mlh_Head;
  1681.             int fi=0, in=0, ar=0, bo=0;
  1682.             BOOL comma=FALSE;
  1683.             for (; cargo->unode.mln_Succ; cargo=(struct Unit *)cargo->unode.mln_Succ)
  1684.                if (cargo->ship==unit)
  1685.                   switch (cargo->type) {
  1686.                      case FIGHTER:
  1687.                         fi++;
  1688.                         break;
  1689.                      case BOMBER:
  1690.                         bo++;
  1691.                         break;
  1692.                      case RIFLE:
  1693.                         in++;
  1694.                         break;
  1695.                      case ARMOR:
  1696.                         ar++;
  1697.                   }
  1698.             strcat(foo,"[");
  1699.             if (fi) {
  1700.                sprintf(bar,"%ld FI");
  1701.                strcat(foo,bar);
  1702.                comma = TRUE;
  1703.             }
  1704.             if (bo) {
  1705.                if (comma)
  1706.                   strcat(foo,",");
  1707.                sprintf(bar,"%ld BO");
  1708.                strcat(foo,bar);
  1709.                comma = TRUE;
  1710.             }
  1711.             if (in) {
  1712.                if (comma)
  1713.                   strcat(foo,",");
  1714.                sprintf(bar,"%ld IN");
  1715.                strcat(foo,bar);
  1716.                comma = TRUE;
  1717.             }
  1718.             if (ar) {
  1719.                if (comma)
  1720.                   strcat(foo,",");
  1721.                sprintf(bar,"%ld AR");
  1722.                strcat(foo,bar);
  1723.             }
  1724.             strcat(foo,"]");
  1725.          }
  1726.          strcat(foo,", ");
  1727.          if (wishbook[unit->type].hitpoints>1) {
  1728.             sprintf(bar,"Hits: %ld/%ld, ",unit->damage,wishbook[unit->type].hitpoints);
  1729.             strcat(foo,bar);
  1730.          }
  1731.          if (wishbook[unit->type].range>0)
  1732.             sprintf(bar,"Rng: %ld/%ld, ",unit->move/60,unit->fuel);
  1733.          else
  1734.             sprintf(bar,"Rng: %ld, ",(unit->move+20)/60);
  1735.          strcat(foo,bar);
  1736.          strcat(foo,"Ord: \n");
  1737.  
  1738.          Write(outfile,foo,(long)strlen(foo));
  1739.       }
  1740.    Close(outfile);
  1741. }
  1742.  
  1743.  
  1744. // end of listing
  1745.